/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Library General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301,  USA
 */

/**
 * @version db_mysql.c 0.0.1
 * @package Comoro
 * @author Dr.NP <np@bsgroup.org>
 * 
 * BS.Play MySQL port
 */

#include "bspd.h"

#include "mysql.h"

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

extern struct conf setting;
MYSQL *mysql_conn;
char mysql_connected = 0x0;
struct mysql_env * mysql_env_list[MAX_MYSQL_ENV];
const char *mysql_db;

/* MySQL initialize */
int db_mysql_init()
{
	if (mysql_connected)
	{
		return RTN_SUCCESS;
	}
	
	mysql_conn = mysql_init(NULL);
	
	const char *mysql_pass = (const char *) setting.mysql_pass;
	const char *mysql_sock = (const char *) setting.mysql_sock;
	mysql_db = (const char *) setting.mysql_default_db;
	
	if (0 == strlen(mysql_pass))
	{
		// For the password, a value of the empty string in the mysql_real_connect() 
		// call cannot be overridden in an option file, because the empty string indicates 
		// explicitly that the MySQL account must have an empty password.
		mysql_pass = (const char *) NULL;
	}
	
	if (0 == strlen(mysql_sock))
	{
		// If no unix sock, specify a value of NULL
		mysql_sock = (const char *) NULL;
	}
	
	if (0 == strlen(mysql_db))
	{
		// No default database
		mysql_db = (const char *) NULL;
	}
	
	if (!mysql_real_connect(mysql_conn, 
							(const char *) setting.mysql_host, 
							(const char *) setting.mysql_user, 
							mysql_pass, 
							mysql_db, 
							0, 
							mysql_sock, 
							0))
	{
		log_add("MySQL connection error: %s", mysql_error(mysql_conn));
		return RTN_GENERAL;
	}
	
	// Set reconnect
	my_bool my_true = 0x1;
	mysql_options(mysql_conn, MYSQL_OPT_RECONNECT, &my_true);
	
	// Set charset
	mysql_query(mysql_conn, "SET NAMES 'utf8'");
	mysql_connected = 0x1;

	fprintf(stderr, "MySQL connection initialized at host %s\n", setting.mysql_host);
	log_add("MySQL connetion initialized at host: %s", setting.mysql_host);
	
	// Clear env array
	int i;
	
	for (i = 0; i < MAX_MYSQL_ENV; i ++)
	{
		mysql_env_list[i] = (struct mysql_env *) NULL;
	}
	
	log_add("MySQL env list initialized, %d tables alloced", MAX_MYSQL_ENV);
	
	return RTN_SUCCESS;
}

/* MySQL close */
int db_mysql_close()
{
	if (mysql_connected)
	{
		mysql_close(mysql_conn);
		mysql_connected = 0x0;
	}
	
	return RTN_SUCCESS;
}

/* MySQL create env */
int db_mysql_create_env(int env_id)
{
	if (!mysql_env_list[env_id])
	{
		// Empty slot
		struct mysql_env *curr = (struct mysql_env *) malloc(sizeof(struct mysql_env));
		
		curr->env_id = env_id;
		curr->db_name = (char *) NULL;
		curr->conn = mysql_conn;
		curr->curr_res = (MYSQL_RES *) NULL;
		curr->num_columns = 0;
		
		mysql_env_list[env_id] = curr;
		
		log_add("MySQL env table <%d> created", env_id);
		
		return env_id;
	}
	
	return RTN_GENERAL;
}

/* Set env db name */
int db_mysql_set_db(int env_id, char *db_name)
{
	if (mysql_env_list[env_id])
	{
		if (mysql_env_list[env_id]->db_name)
		{
			free(mysql_env_list[env_id]->db_name);
		}
		
		mysql_env_list[env_id]->db_name = db_name;
	}
	
	return RTN_SUCCESS;
}

/* MySQL unset env */
int db_mysql_close_env(int env_id)
{
	if (mysql_env_list[env_id])
	{
		if (mysql_env_list[env_id]->db_name)
		{
			free(mysql_env_list[env_id]->db_name);
		}
		
		free(mysql_env_list[env_id]);
	}
	
	mysql_env_list[env_id] = (struct mysql_env *) NULL;
	
	log_add("MySQL env table <%d> destroied", env_id);
	
	return env_id;
}

/* MySQL SQL execute */
int db_mysql_query(int env_id, const char *query)
{
	static char buff[256];
	
	if (!mysql_env_list[env_id])
	{
		// Empty env
		return RTN_GENERAL;
	}
	
	if (0x0 == mysql_connected)
	{
		// Not connected
		fprintf(stderr, "MYSQL ERROR\n");
		return RTN_GENERAL;
	}
	
	if (mysql_env_list[env_id]->db_name)
	{
		// Set current db
		sprintf(buff, "USE %s", mysql_env_list[env_id]->db_name);
		mysql_query(mysql_env_list[env_id]->conn, buff);
	}
	
	else if (mysql_db)
	{
		sprintf(buff, "USE %s", mysql_db);
		mysql_query(mysql_env_list[env_id]->conn, buff);
	}
	
	if (mysql_query(mysql_env_list[env_id]->conn, query))
	{
		// SQL error
		return RTN_GENERAL;
	}
	
	// Store result
	if (mysql_env_list[env_id]->curr_res)
	{
		mysql_free_result(mysql_env_list[env_id]->curr_res);
	}
	
	mysql_env_list[env_id]->curr_res = mysql_store_result(mysql_env_list[env_id]->conn);
	
	return RTN_SUCCESS;
}

/* MySQL fetch row */
struct mysql_row ** db_mysql_fetch_row(int env_id, int *num_columns)
{
	MYSQL_ROW row;
	MYSQL_FIELD *fields;
	unsigned int num_fields;
	unsigned long *lengths;
	unsigned int i;
	struct mysql_row **ret;
	struct mysql_row *curr;
	
	if (!mysql_env_list[env_id] || !mysql_env_list[env_id]->curr_res)
	{
		// Empty res
		*num_columns = 0;
		return (struct mysql_row **) NULL;
	}
	
	row = mysql_fetch_row(mysql_env_list[env_id]->curr_res);
	if (!row)
	{
		// Last row
		*num_columns = 0;
		return (struct mysql_row **) NULL;
	}
	
	// Generate an array
	num_fields = mysql_num_fields(mysql_env_list[env_id]->curr_res);
	fields = mysql_fetch_fields(mysql_env_list[env_id]->curr_res);
	lengths = mysql_fetch_lengths(mysql_env_list[env_id]->curr_res);
	
	ret = (struct mysql_row **) malloc(sizeof(struct mysql_row *) * num_fields);
	
	for (i = 0; i < num_fields; i ++)
	{
		curr = (struct mysql_row *) malloc(sizeof(struct mysql_row));
		curr->name = fields[i].name ? strdup(fields[i].name) : "";
		curr->value = (row[i] ? strdup(row[i]) : (char *) NULL);
		curr->length = lengths[i];
		
		ret[i] = curr;
	}
	
	mysql_env_list[env_id]->num_columns = num_fields;
	*num_columns = num_fields;
	
	return ret;
}

/* Clean a row struct */
void db_mysql_del_row(struct mysql_row **row, int num_columns)
{
	int i;

	if (!row)
	{
		return;
	}

	for (i = 0; i < num_columns; i ++)
	{
		if (row[i])
		{
			if (row[i]->name)
			{
				free(row[i]->name);
			}

			if (row[i]->value)
			{
				free(row[i]->value);
			}

			free(row[i]);
		}
	}
	
	free(row);
	
	return;
}

/* MySQL get num_columns */
int db_mysql_num_columns(int env_id)
{
	return (mysql_env_list[env_id]) ? (mysql_env_list[env_id]->num_columns) : 0;
}
